//! Call pattern proto defs for device message queues.

use aspect_codec::prelude::*;
use aspect_core::message_queue as coremq;
use aspect_ident::DeviceIdent;

use crate::user::UserCreds;

/// List the queues available to the calling device ID, and the relative
/// positions of all of them.
pub struct MqListProto;

/*impl Rpc for MqListProto {
    const TOPIC: Topic = Topic::from_const_str("MQLS");
    type Request = MqListRequest;
    type Response = MqListResponse;
}*/

#[derive(Clone, Debug)]
pub struct MqListRequest {
    // temporary
    pub creds: UserCreds,

    // temporary
    pub device_ident: DeviceIdent,
}

aspect_codec::inst_struct_codec! {
    MqListRequest {
        creds: UserCreds,
        device_ident: DeviceIdent,
    }
}

#[derive(Clone, Debug)]
pub struct MqListResponse {
    pub queues: Vec<QueueViewState>,
}

aspect_codec::inst_semimanual_codec! {
    MqListResponse {
        queues vec QueueViewState
    }
}

#[derive(Clone, Debug)]
pub struct QueueViewState {
    /// Device ID.
    pub id: coremq::MessageQueueId,

    /// Position this device has read up to.
    pub device_pos: u64,

    /// Position of the last message in the queue.
    pub queue_pos: u64,
}

aspect_codec::inst_struct_codec! {
    QueueViewState {
        id: coremq::MessageQueueId,
        device_pos: u64,
        queue_pos: u64,
    }
}

/// Fetches a range of messages from the message queue.
// TODO this protocol should have a subscription based alternative
pub struct MqPullProto;

#[derive(Clone, Debug)]
pub struct MqFetchRange {
    // temporary
    pub creds: UserCreds,

    // temporary
    pub device_id: DeviceIdent,

    pub queue_id: coremq::MessageQueueId,
    pub start_idx: u64,
    pub end_idx: u64,
}

aspect_codec::inst_struct_codec! {
    MqFetchRange {
        creds: UserCreds,
        device_id: DeviceIdent,
        queue_id: coremq::MessageQueueId,
        start_idx: u64,
        end_idx: u64,
    }
}

/// A group of queue messages.
#[derive(Clone, Debug)]
pub struct MqEntries {
    /// The current queue position gets included here again to update cursors
    /// more efficiently.
    pub cur_queue_pos: u64,

    /// The entries they actually asked for.
    pub entries: Vec<coremq::QueueEntry>,
}

aspect_codec::inst_semimanual_codec! {
    MqEntries {
        cur_queue_pos field u64,
        entries vec coremq::QueueEntry,
    }
}

/// Updates the cursor position for the specific reader on a queue.
pub struct MqConsumeProto;

/*impl Rpc for MqConsumeProto {
    const TOPIC: Topic = Topic::from_const_str("MQCS");
    type Request = ConsumeRequest;
    type Response = ();
}*/

#[derive(Debug, Clone)]
pub struct ConsumeRequest {
    // temporary
    pub creds: UserCreds,

    // temporary
    pub device_id: DeviceIdent,

    pub queue_id: coremq::MessageQueueId,
    pub consume_pos: u64,
}

aspect_codec::inst_struct_codec! {
    ConsumeRequest {
        creds: UserCreds,
        device_id: DeviceIdent,
        queue_id: coremq::MessageQueueId,
        consume_pos: u64,
    }
}

pub struct MqDirectDeviceSendProto;

/*impl Rpc for MqDirectDeviceSendProto {
    const TOPIC: Topic = Topic::from_const_str("MQDD");
    type Request = MqDirectDeviceSendRequest;
    type Response = ();
}*/

#[derive(Debug, Clone)]
pub struct MqDirectDeviceSendRequest {
    // temporary
    pub creds: UserCreds,

    // temporary
    pub device_id: DeviceIdent,

    // TODO IMPORTANT MAKE THIS IDEMPOTENT
    pub target_device_id: DeviceIdent,
    pub payload: Vec<u8>,
}

aspect_codec::inst_struct_codec! {
    MqDirectDeviceSendRequest {
        creds: UserCreds,
        device_id: DeviceIdent,
        target_device_id: DeviceIdent,
        payload: Vec<u8>,
    }
}
